#include <stdarg.h>
#include <stdio.h>

#include "mips.h"
#include "../hle.h"

#define RN(i) registers[i]
#define FN(i) fregisters[i]
#define STN(i) cop0streg[i]
#define CTLN(i) cop0ctlreg[i]

#define DBGFUNC(fn) void fn(char *out, u32 len, u32 op)

void intdesc(char *out, u32 len, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(out, len, fmt, ap);
    va_end(ap);
}

DBGFUNC(dbg_jal) { intdesc(out, len, "jal 0x%08x", (PC & 0xf0000000) | ((op & 0x3ffffff) << 2)); }
DBGFUNC(dbg_jalr) { intdesc(out, len, "jalr $%s", RN(_RS)); }
DBGFUNC(dbg_j) { intdesc(out, len, "j 0x%08x",   (PC & 0xf0000000) | ((op & 0x3ffffff) << 2)); }
DBGFUNC(dbg_jr) { intdesc(out, len, "jr $%s", RN(_RS)); }
DBGFUNC(dbg_syscall) { intdesc(out, len, "syscall 0x%x [%s]", op >> 6, hle_getSyscallName(op >> 6)); }
DBGFUNC(dbg_beq) { intdesc(out, len, "beq $%s, $%s, 0x%08x", RN(_RS), RN(_RT), BRANCHADDR); }
DBGFUNC(dbg_bne) { intdesc(out, len, "bne $%s, $%s, 0x%08x", RN(_RS), RN(_RT), BRANCHADDR); }
DBGFUNC(dbg_blez) { intdesc(out, len, "blez $%s, 0x%08x", RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_blezl) { intdesc(out, len, "blezl $%s, 0x%08x", RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_bltz) { intdesc(out, len, "bltz $%s, 0x%08x", RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_bltzl) { intdesc(out, len, "bltzl $%s, 0x%08x", RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_beql) { intdesc(out, len, "beql $%s, $%s, 0x%08x", RN(_RT), RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_bnel) { intdesc(out, len, "bnel $%s, $%s, 0x%08x", RN(_RT), RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_bgez) { intdesc(out, len, "bgez $%s, 0x%08x", RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_bgezl) { intdesc(out, len, "bgezl $%s, 0x%08x", RN(_RS), BRANCHADDR); }
DBGFUNC(dbg_addi) { intdesc(out, len, "addi $%s, $%s, %d", RN(_RT), RN(_RS), SOFFSET); }
DBGFUNC(dbg_addiu) { intdesc(out, len, "addiu $%s, $%s, %d", RN(_RT), RN(_RS), SOFFSET); }
DBGFUNC(dbg_andi) { intdesc(out, len, "andi $%s, $%s, %d", RN(_RT), RN(_RS), SOFFSET); }
DBGFUNC(dbg_ori) { intdesc(out, len, "ori $%s, $%s, %d", RN(_RT), RN(_RS), UOFFSET); }
DBGFUNC(dbg_lui) { intdesc(out, len, "lui $%s, 0x%04x", RN(_RT), (u16)SOFFSET); }
DBGFUNC(dbg_add) { intdesc(out, len, "add $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_addu) { intdesc(out, len, "addu $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_and) { intdesc(out, len, "and $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_or) { intdesc(out, len, "or $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_sllv) { intdesc(out, len, "sllv $%s, $%s, $%s", RN(_RD), RN(_RT), RN(_RS)); }
DBGFUNC(dbg_srl) { intdesc(out, len, "srl $%s, $%s, %d", RN(_RD), RN(_RT), _SA); }
DBGFUNC(dbg_srlv) { intdesc(out, len, "srlv $%s, $%s, $%s", RN(_RD), RN(_RT), RN(_RS)); }
DBGFUNC(dbg_rotr) { intdesc(out, len, "rotr $%s, $%s, %d", RN(_RD), RN(_RT), _SA); }
DBGFUNC(dbg_rotrv) { intdesc(out, len, "rotrv $%s, $%s, $%s", RN(_RD), RN(_RT), RN(_RS)); }
DBGFUNC(dbg_srav) { intdesc(out, len, "srav $%s, $%s, $%s", RN(_RD), RN(_RT), RN(_RS)); }
DBGFUNC(dbg_slt) { intdesc(out, len, "slt $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_sltu) { intdesc(out, len, "sltu $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_slti) { intdesc(out, len, "slti $%s, $%s, %d", RN(_RT), RN(_RS), SOFFSET); }
DBGFUNC(dbg_sltiu) { intdesc(out, len, "sltiu $%s, $%s, %d", RN(_RT), RN(_RS), SOFFSET); }
DBGFUNC(dbg_lw) { intdesc(out, len, "lw $%s, %d($%s) [%08x <- %08x]", RN(_RT), SOFFSET, RN(_RS), mem_isOk(MEMADDR) ? mem_read32(MEMADDR) : -1, MEMADDR); }
DBGFUNC(dbg_lwc1) { intdesc(out, len, "lwc1 $%s, %d($%s)", FN(_FT), SOFFSET, RN(_RS)); }
DBGFUNC(dbg_lwl) { intdesc(out, len, "lwl $%s, %d($%s)", RN(_RT), SOFFSET, RN(_RS)); }
DBGFUNC(dbg_lwr) { intdesc(out, len, "lwr $%s, %d($%s)", RN(_RT), SOFFSET, RN(_RS)); }
DBGFUNC(dbg_lb) { intdesc(out, len, "lb $%s, %d($%s) [%02x <- %08x]", RN(_RT), SOFFSET, RN(_RS), mem_isOk(MEMADDR) ? mem_read8(MEMADDR) : -1, MEMADDR); }
DBGFUNC(dbg_lbu) { intdesc(out, len, "lbu $%s, %d($%s) [%02x <- %08x]", RN(_RT), SOFFSET, RN(_RS), mem_isOk(MEMADDR) ? mem_read8(MEMADDR) : -1, MEMADDR); }
DBGFUNC(dbg_lh) { intdesc(out, len, "lh $%s, %d($%s) [%04x <- %08x]", RN(_RT), SOFFSET, RN(_RS), mem_isOk(MEMADDR) ? mem_read16(MEMADDR) : -1, MEMADDR); }
DBGFUNC(dbg_lhu) { intdesc(out, len, "lhu $%s, %d($%s) [%04x <- %08x]", RN(_RT), SOFFSET, RN(_RS), mem_isOk(MEMADDR) ? mem_read16(MEMADDR) : -1, MEMADDR); }
DBGFUNC(dbg_sb) { intdesc(out, len, "sb $%s, %d($%s) [%02x -> %08x]", RN(_RT), SOFFSET, RN(_RS), (u8)(R(_RT) & 0xff), MEMADDR); }
DBGFUNC(dbg_sw) { intdesc(out, len, "sw $%s, %d($%s) [%08x -> %08x]", RN(_RT), SOFFSET, RN(_RS), R(_RT), MEMADDR); }
DBGFUNC(dbg_swc1) { intdesc(out, len, "swc1 $%s, %d($%s)", FN(_FT), SOFFSET, RN(_RS)); }
DBGFUNC(dbg_swl) { intdesc(out, len, "swl $%s, %d($%s)", RN(_RT), SOFFSET, RN(_RS)); }
DBGFUNC(dbg_swr) { intdesc(out, len, "swr $%s, %d($%s)", RN(_RT), SOFFSET, RN(_RS)); }
DBGFUNC(dbg_sh) { intdesc(out, len, "sh $%s, %d($%s) [%04x -> %08x]", RN(_RT), SOFFSET, RN(_RS), (u16)(R(_RT) & 0xffff), MEMADDR); }

DBGFUNC(dbg_sll)
{
    if (op == 0)
        intdesc(out, len, "nop");
    else
        intdesc(out, len, "sll $%s, $%s, %d", RN(_RD), RN(_RT), _FD);
}

DBGFUNC(dbg_abs) { intdesc(out, len, "abs $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_fadd) { intdesc(out, len, "add.s $%s, $%s, $%s", FN(_FD), FN(_FS), FN(_FT)); }
DBGFUNC(dbg_fsub) { intdesc(out, len, "sub.s $%s, $%s, $%s", FN(_FD), FN(_FS), FN(_FT)); }
DBGFUNC(dbg_fmul) { intdesc(out, len, "mul.s $%s, $%s, $%s", FN(_FD), FN(_FS), FN(_FT)); }
DBGFUNC(dbg_fdiv) { intdesc(out, len, "div.s $%s, $%s, $%s", FN(_FD), FN(_FS), FN(_FT)); }
DBGFUNC(dbg_mov) { intdesc(out, len, "mov.s $%s, $%s, $%s", FN(_FD), FN(_FS), FN(_FT)); }
DBGFUNC(dbg_bgtz) { intdesc(out, len, "bgtz $%s, 0x%08x", RN(_RS), MEMADDR); }
DBGFUNC(dbg_bgtzl) { intdesc(out, len, "bgtzl $%s, 0x%08x", RN(_RS), MEMADDR); }
DBGFUNC(dbg_xor) { intdesc(out, len, "xor $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_xori) { intdesc(out, len, "xori $%s, $%s, %d", RN(_RT), RN(_RS), SOFFSET); }
DBGFUNC(dbg_cache) { intdesc(out, len, "cache %d, %d($%s)", _RT, SOFFSET, RN(_RS)); }
DBGFUNC(dbg_movn) { intdesc(out, len, "movn $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_movz) { intdesc(out, len, "movz $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_sra) { intdesc(out, len, "sra $%s, $%s, $%s", RN(_RD), RN(_RT), RN(_SA)); }
DBGFUNC(dbg_pref) { intdesc(out, len, "pref %d, %d($%s)", _RT, SOFFSET, RN(_RS)); }
DBGFUNC(dbg_mthi) { intdesc(out, len, "mthi $%s", RN(_RS)); }
DBGFUNC(dbg_mtlo) { intdesc(out, len, "mtlo $%s", RN(_RS)); }
DBGFUNC(dbg_mfhi) { intdesc(out, len, "mfhi $%s", RN(_RD)); }
DBGFUNC(dbg_mflo) { intdesc(out, len, "mflo $%s", RN(_RD)); }
DBGFUNC(dbg_nor) { intdesc(out, len, "nor $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_mult) { intdesc(out, len, "mult $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_multu) { intdesc(out, len, "multu $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_div) { intdesc(out, len, "div $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_divu) { intdesc(out, len, "divu $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_sub) { intdesc(out, len, "sub $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_subu) { intdesc(out, len, "subu $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_break) { if (op) intdesc(out, len, "break"); }
DBGFUNC(dbg_min) { intdesc(out, len, "min $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_max) { intdesc(out, len, "max $%s, $%s, $%s", RN(_RD), RN(_RS), RN(_RT)); }
DBGFUNC(dbg_seb) { intdesc(out, len, "seb $%s, $%s", RN(_RD), RN(_RT)); }
DBGFUNC(dbg_seh) { intdesc(out, len, "seh $%s, $%s", RN(_RD), RN(_RT)); }
DBGFUNC(dbg_ins) { intdesc(out, len, "ins $%s, $%s, 0x%02x, 0x%02x", RN(_RT), RN(_RS), _POS, _END - _POS + 1); }
DBGFUNC(dbg_ext) { intdesc(out, len, "ext $%s, $%s, 0x%02x, 0x%02x", RN(_RT), RN(_RS), _POS, _END + 1); }
DBGFUNC(dbg_clz) { intdesc(out, len, "clz $%s, $%s", RN(_RD), RN(_RS)); }
DBGFUNC(dbg_clo) { intdesc(out, len, "clo $%s, $%s", RN(_RD), RN(_RS)); }
DBGFUNC(dbg_madd) { intdesc(out, len, "madd $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_maddu) { intdesc(out, len, "maddu $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_msub) { intdesc(out, len, "msub $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_msubu) { intdesc(out, len, "msubu $%s, $%s", RN(_RS), RN(_RT)); }
DBGFUNC(dbg_mfic) { intdesc(out, len, "mfic $%s", RN(_RT)); }
DBGFUNC(dbg_mtic) { intdesc(out, len, "mtic $%s", RN(_RT)); }
DBGFUNC(dbg_wsbh) { intdesc(out, len, "wsbh $%s, $%s", RN(_RD), RN(_RT)); }
DBGFUNC(dbg_wsbw) { intdesc(out, len, "wsbw $%s, $%s", RN(_RD), RN(_RT)); }
DBGFUNC(dbg_bitrev) { intdesc(out, len, "bitrev $%s, $%s", RN(_RD), RN(_RT)); }

DBGFUNC(dbg_mfc0) { intdesc(out, len, "mfc0 $%s, $%s", RN(_RT), STN(_RD)); }
DBGFUNC(dbg_mtc0) { intdesc(out, len, "mtc0 $%s, $%s", RN(_RT), STN(_RD)); }
DBGFUNC(dbg_cfc0) { intdesc(out, len, "cfc0 $%s, $%s", RN(_RT), CTLN(_RD)); }
DBGFUNC(dbg_ctc0) { intdesc(out, len, "ctc0 $%s, $%s", RN(_RT), CTLN(_RD)); }

DBGFUNC(dbg_bc1f) { intdesc(out, len, "bc1f 0x%08x", BRANCHADDR); }
DBGFUNC(dbg_bc1fl) { intdesc(out, len, "bc1fl 0x%08x", BRANCHADDR); }
DBGFUNC(dbg_bc1t) { intdesc(out, len, "bc1t 0x%08x", BRANCHADDR); }
DBGFUNC(dbg_bc1tl) { intdesc(out, len, "bc1tl 0x%08x", BRANCHADDR); }
DBGFUNC(dbg_mfc1) { intdesc(out, len, "mfc1 $%s, $%s", RN(_RT), FN(_FS)); }
DBGFUNC(dbg_mtc1) { intdesc(out, len, "mtc1 $%s, $%s", RN(_RT), FN(_FS)); }
DBGFUNC(dbg_cfc1) { intdesc(out, len, "cfc1 $%s, $%s", RN(_RT), FN(_FS)); }
DBGFUNC(dbg_ctc1) { intdesc(out, len, "cfc1 $%s, $%s", RN(_RT), FN(_FS)); }

DBGFUNC(dbg_sqrt) { intdesc(out, len, "sqrt $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_neg) { intdesc(out, len, "neg $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_round) { intdesc(out, len, "round $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_trunc) { intdesc(out, len, "trunc $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_ceil) { intdesc(out, len, "ceil $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_floor) { intdesc(out, len, "floor $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_cvtsw) { intdesc(out, len, "cvt.s.w $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_cvtws) { intdesc(out, len, "cvt.w.s $%s, $%s", FN(_FD), FN(_FS)); }
DBGFUNC(dbg_eq) { intdesc(out, len, "c.eq.s $%s, $%s", FN(_FS), FN(_FT)); }
DBGFUNC(dbg_lt) { intdesc(out, len, "c.lt.s $%s, $%s", FN(_FS), FN(_FT)); }
DBGFUNC(dbg_nge) { intdesc(out, len, "c.nge.s $%s, $%s", FN(_FS), FN(_FT)); }
DBGFUNC(dbg_le) { intdesc(out, len, "c.le.s $%s, $%s", FN(_FS), FN(_FT)); }
DBGFUNC(dbg_ngt) { intdesc(out, len, "c.ngt.s $%s, $%s", FN(_FS), FN(_FT)); }

DBGFUNC(dbg_mfv) { intdesc(out, len, "mfv $%s, $S%03d", RN(_RT), _VR); }
DBGFUNC(dbg_mtv) { intdesc(out, len, "mtv $%s, $S%03d", RN(_RT), _VR); }
DBGFUNC(dbg_bvf) { intdesc(out, len, "bvf %03d, 0x%08x", _CC, BRANCHADDR); }
DBGFUNC(dbg_bvfl) { intdesc(out, len, "bvf %03d, 0x%08x", _CC, BRANCHADDR); }
DBGFUNC(dbg_bvt) { intdesc(out, len, "bvf %03d, 0x%08x", _CC, BRANCHADDR); }
DBGFUNC(dbg_bvtl) { intdesc(out, len, "bvf %03d, 0x%08x", _CC, BRANCHADDR); }

